package cn.bearspark.happening.provider.dao.buffer;

import cn.bearspark.common.parse.host.HostParser;
import cn.bearspark.common.parse.host.HostPipeline;
import cn.bearspark.common.parse.host.HostTextContext;
import cn.bearspark.framework.redis.starter.buffer.RedisBuffer;
import cn.bearspark.happening.provider.dao.key.HappeningBufferKeyBuilder;
import cn.bearspark.happening.provider.dao.mapper.HappeningImgMapper;
import cn.bearspark.happening.provider.dao.po.HappeningImgPO;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 动态图片路径的缓存器
 *
 * @author f
 */
@Component
public class HappeningImgBuffer extends RedisBuffer {

    /**
     * 动态图片缓存的过期时间，默认为 5min
     */
    private static final int IMG_EXPIRE = 5;

    /**
     * 解析主机 ip 地址的管道 TODO 想办法优化 @AutoParseHost
     */
    private final HostPipeline hostPipeline = (HostPipeline) new HostPipeline()
            .add(new HostParser());

    private final HappeningImgMapper happeningImgMapper;

    private final HappeningBufferKeyBuilder happeningBufferKeyBuilder;

    public HappeningImgBuffer(RedisTemplate<String, Object> redisTemplate,
                              HappeningImgMapper happeningImgMapper,
                              HappeningBufferKeyBuilder happeningBufferKeyBuilder) {
        super(redisTemplate);
        this.happeningImgMapper = happeningImgMapper;
        this.happeningBufferKeyBuilder = happeningBufferKeyBuilder;
    }

    /**
     * 获取指定动态的图片
     *
     * @param happeningIdSet 指定的动态 id 的 Set 集合
     * @return 指定动态的图片映射，key：动态 id，value：动态的图片集合
     */
    public Map<Long, List<String>> map(Set<Long> happeningIdSet) {
        // 从缓存中查找动态的图片，并将缺失图片的动态 id 保存起来
        List<Long> missingHappeningIdList = new ArrayList<>();
        Map<Long, List<String>> happeningImgMap = happeningIdSet.stream().collect(Collectors.toMap(
                Function.identity(),
                happeningId -> {
                    Set<Object> members = redisTemplate.opsForSet().members(
                            happeningBufferKeyBuilder.buildHappeningImgKey(happeningId));
                    if (members.isEmpty()) {
                        missingHappeningIdList.add(happeningId);
                        return new ArrayList<>();
                    } else {
                        return members.stream()
                                .map(obj -> hostPipeline.unparse(new HostTextContext((String) obj))).toList();
                    }
                }
        ));

        // 如果没有缺失图片的动态，则直接返回
        if (missingHappeningIdList.isEmpty()) {
            return happeningImgMap;
        }

        // 将从数据库中找到的数据分组
        List<HappeningImgPO> happeningImgPOList = happeningImgMapper.listByHappeningIdList(missingHappeningIdList);
        Map<Long, List<String>> missingHappeningImgMap = new HashMap<>(happeningImgPOList.size());
        int size = happeningImgPOList.size();
        for (int i = 0; i < size; i++) {
            long happeningId = happeningImgPOList.get(i).getHappeningId();
            int j = i + 1;
            while (j < size && happeningImgPOList.get(j).getHappeningId() == happeningId) {
                j++;
            }

            missingHappeningImgMap.put(happeningId,
                    happeningImgPOList.subList(i, j).stream().map(HappeningImgPO::getImgUrl).toList());

            i = j;
        }

        // 缓存从数据库中找到的数据
        super.executePipelined(operations -> missingHappeningImgMap.forEach((happeningId, imgList) -> {
            String key = happeningBufferKeyBuilder.buildHappeningImgKey(happeningId);
            operations.opsForSet().add(key, imgList.toArray(new String[0]));
            operations.expire(key, IMG_EXPIRE, TimeUnit.MINUTES);
        }));

        // 将从数据库中找到的数据填入 happeningImgMap 中，并返回
        missingHappeningImgMap.forEach((happeningId, imgList) -> happeningImgMap.put(happeningId, imgList.stream()
                .map(imgUrl -> hostPipeline.unparse(new HostTextContext(imgUrl))).toList()
        ));
        return happeningImgMap;
    }

    /**
     * 清理指定动态的图片缓存
     *
     * @param happeningId 指定的动态 id
     */
    public void clean(long happeningId) {
        redisTemplate.delete(happeningBufferKeyBuilder.buildHappeningImgKey(happeningId));
    }
}
